package com.alks.function.service.impl.biocontrol.deptwork;

import com.alibaba.excel.util.StringUtils;
import com.alks.common.aop.AutoPageAop;
import com.alks.common.data.dto.SysUserTokenDTO;
import com.alks.common.data.thread.UserIdThread;
import com.alks.common.utils.beanutils.ArrayUtils;
import com.alks.entity.data.entity.PcCuttingLsHead;
import com.alks.entity.data.entity.PcLotLtTime;
import com.alks.entity.data.entity.PsScheduleHead;
import com.alks.entity.data.entity.PsScheduleLot;
import com.alks.entity.data.page.PageRecord;
import com.alks.entity.exception.ServiceErrorException;
import com.alks.function.data.dto.biocontrol.webwork.MoldProgTempDto;
import com.alks.function.data.dto.biocontrol.webwork.SchLineDto;
import com.alks.function.data.request.biocontrol.webwork.CancelSchRequest;
import com.alks.function.data.request.biocontrol.webwork.MoldProgtTempRequest;
import com.alks.function.data.vo.biocontrol.webWrok.LsSchVo;
import com.alks.function.mapper.biocontrol.deptwork.BC_DW_PcLotLtTimeMapper;
import com.alks.function.mapper.biocontrol.webwork.PcCuttingLsHeadMapper;
import com.alks.function.mapper.biocontrol.webwork.PsLineMapper;
import com.alks.function.mapper.biocontrol.webwork.PsScheduleHeadMapper;
import com.alks.function.mapper.biocontrol.webwork.PsScheduleLotMapper;
import com.alks.function.service.biocontrol.deptwork.IMoldProgService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.github.pagehelper.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author MS
 * @since 2024/10/5下午6:20
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class MoldProgServiceImpl implements IMoldProgService {
    private final PsScheduleLotMapper lotMapper;
    private final PsLineMapper lineMapper;
    private final PsScheduleHeadMapper headMapper;
    private final PcCuttingLsHeadMapper lsHeadMapper;
    private final BC_DW_PcLotLtTimeMapper ltTimeMapper;

    @Override
    @AutoPageAop
    public PageRecord<MoldProgTempDto> temp(MoldProgtTempRequest request) {
        String companyId = UserIdThread.get().getCompanyId();
        /*获取数据*/
        List<MoldProgTempDto> dtoList = lotMapper.unSchLotTempQuery(request, companyId);
        /*获取求和*/
        List<Map<String, Object>> sumList = lotMapper.unSchLotTempSumQuery(request, companyId);
        /*封装返回*/
        PageRecord<MoldProgTempDto> pageRecord = new PageRecord<>(dtoList, sumList);
        if (request.getPageSize() != -1 && request.getPageNum() != -1) {
            Page<MoldProgTempDto> page = (Page<MoldProgTempDto>) dtoList;
            pageRecord.setTotal(page.getTotal());
        }
        return pageRecord;
    }

    @Override
    public List<SchLineDto> lineTemp() {
        String companyId = UserIdThread.get().getCompanyId();
        /*获取数据*/
        List<SchLineDto> dtoList = lineMapper.schLineQuery(companyId);
        return dtoList;
    }

    @Override
    @AutoPageAop
    public PageRecord<PsScheduleHead> detail(MoldProgtTempRequest request) {
        String companyId = UserIdThread.get().getCompanyId();
        /*获取数据*/
        List<PsScheduleHead> headList = headMapper.schDetailQuery(request, companyId);
        /*获取求和*/
        List<Map<String, Object>> sumList = headMapper.schDetailSumQuery(request, companyId);
        /*封装返回*/
        PageRecord<PsScheduleHead> pageRecord = new PageRecord<>(headList, sumList);
        if (request.getPageSize() != -1 && request.getPageNum() != -1) {
            Page<PsScheduleHead> page = (Page<PsScheduleHead>) headList;
            pageRecord.setTotal(page.getTotal());
        }
        return pageRecord;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void upLot() {
        SysUserTokenDTO tokenDTO = UserIdThread.get();
        String companyId = tokenDTO.getCompanyId();
        String userName = tokenDTO.getUserName();
        /*写入新指令*/
        //MS TODO 2024/10/8 : 加入期限，如只更新最近半年指令
        lotMapper.upLotIns(companyId, userName);
        /*更新库存*/
        lotMapper.upLotStk(companyId);
        /*去除不存在的数据*/
        //MS TODO 2024/10/8 : 是否删除LOT_NO重复的数据
        lotMapper.upLotDel(companyId);
        log.warn("时间：{}", LocalTime.now());
    }

    @Override
    public List<PcCuttingLsHead> lsQuery(String lotNo) {
        String companyId = UserIdThread.get().getCompanyId();
        /*获取数据*/
        LambdaQueryWrapper<PcCuttingLsHead> wrapper = new LambdaQueryWrapper<PcCuttingLsHead>()
                .eq(PcCuttingLsHead::getLotNo, lotNo)
                .eq(PcCuttingLsHead::getCompanyId, companyId);
        return lsHeadMapper.selectList(wrapper);
    }

    @Override
    public void lsSch(String lotNo, List<Integer> lsNoList, String line, BigDecimal seqNo) {
        SysUserTokenDTO tokenDTO = UserIdThread.get();
        String companyId = tokenDTO.getCompanyId();
        String userName = tokenDTO.getUserName();
        List<String> lotNoList = new ArrayList<>();
        lotNoList.add(lotNo);
        /*入参检测，获取基础数据(逻辑处理后一条指令应该只对应一条数据)*/
        PsScheduleLot schLot = schCheck(lotNoList, companyId).get(0);
        /*判断批次是否能排产*/
        List<PcCuttingLsHead> headList = lsCheck(lotNo, lsNoList, seqNo, companyId);
        /*获取产线最大号码*/
        LambdaQueryWrapper<PsScheduleHead> schHeadWrapper = new LambdaQueryWrapper<PsScheduleHead>()
                .select(PsScheduleHead::getSeqNo)
                .eq(PsScheduleHead::getLine, line)
                .eq(PsScheduleHead::getCompanyId, companyId)
                .orderByDesc(PsScheduleHead::getSeqNo)
                .last("LIMIT 1");
        PsScheduleHead head = headMapper.selectOne(schHeadWrapper);
        /*对不正确的号码进行修正*/
        BigDecimal maxSeqNo = head == null ? BigDecimal.ZERO : head.getSeqNo();
        boolean seqFlag = seqNo.compareTo(maxSeqNo) > 0 || seqNo.compareTo(BigDecimal.ZERO) < 0;
        BigDecimal newSeqNo = seqFlag ? maxSeqNo.add(BigDecimal.ONE) : seqNo;
        if (!seqFlag) {
            /*对后续序号进行移序*/
            int seqAdd = lsNoList.size();
            headMapper.seqNoShift(line,newSeqNo, seqAdd,companyId);
        }
        /*写入新数据*/
        List<LsSchVo> voList = headList.stream().map(a -> {
            LsSchVo lsSchVo = new LsSchVo();
            lsSchVo.setLsNo(a.getLsNo());
            lsSchVo.setQty(a.getQty());
            return lsSchVo;
        }).collect(Collectors.toList());
        headMapper.lsSch(schLot,line,voList,newSeqNo, userName);
        /*更新lot表*/
        List<PsScheduleLot> schLotList = new ArrayList<>(1);
        Integer lsQtySum = voList.stream()
                .map(LsSchVo::getQty)
                .reduce(0, Integer::sum);
        schLot.setUnSchQty(BigDecimal.valueOf(lsQtySum));
        lotMapper.allSchUp(schLotList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void allSch(List<String> lotNoList, String line, BigDecimal seqNo) {
        SysUserTokenDTO tokenDTO = UserIdThread.get();
        String companyId = tokenDTO.getCompanyId();
        String userName = tokenDTO.getUserName();
        /*入参检测，获取基础数据*/
        List<PsScheduleLot> schLotList = schCheck(lotNoList, companyId);
        /*获取产线最大号码*/
        LambdaQueryWrapper<PsScheduleHead> schHeadWrapper = new LambdaQueryWrapper<PsScheduleHead>()
                .select(PsScheduleHead::getSeqNo)
                .eq(PsScheduleHead::getLine, line)
                .eq(PsScheduleHead::getCompanyId, companyId)
                .orderByDesc(PsScheduleHead::getSeqNo)
                .last("LIMIT 1");
        PsScheduleHead head = headMapper.selectOne(schHeadWrapper);
        /*对不正确的号码进行修正*/
        BigDecimal maxSeqNo = head == null ? BigDecimal.ZERO : head.getSeqNo();
        boolean seqFlag = seqNo.compareTo(maxSeqNo) > 0 || seqNo.compareTo(BigDecimal.ZERO) < 0;
        BigDecimal newSeqNo = seqFlag ? maxSeqNo.add(BigDecimal.ONE) : seqNo;
        if (!seqFlag) {
            /*对后续序号进行移序*/
            int seqAdd = lotNoList.size();
            headMapper.seqNoShift(line,newSeqNo, seqAdd,companyId);
        }
        /*写入新数据*/
        headMapper.allSch(schLotList,line,newSeqNo, userName);
        /*更新生产天数和始末时间*/
        List<BigDecimal> seqList = new ArrayList<>(lotNoList.size());
        for (int i = 0; i < lotNoList.size(); i++) {
            seqList.add(newSeqNo.add(BigDecimal.valueOf(i)));

        }
//        LocalDateTime startTime = headMapper.getEndTime(companyId,line);
//        headMapper.allSchDateUp(line,seqList,companyId);
        /*更新lot表*/
        lotMapper.allSchUp(schLotList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancelSch(List<CancelSchRequest> request) {
        String companyId = UserIdThread.get().getCompanyId();
        /*核验数据真实性*/
        LambdaQueryWrapper<PsScheduleHead> headWrapper = new LambdaQueryWrapper<>();
        boolean flag = false;
        for (CancelSchRequest r : request) {
            if (flag) {headWrapper.or();}
            headWrapper.and(w -> w.eq(PsScheduleHead::getLine, r.getLine()).eq(PsScheduleHead::getSeqNo, r.getSeqNo()));
            flag = true;
        }
        headWrapper.eq(PsScheduleHead::getCompanyId,companyId);
        List<PsScheduleHead> headList = headMapper.selectList(headWrapper);
        if (ArrayUtils.isEmpyt(headList)){
            throw new ServiceErrorException("该产线下没有这些序号");
        }
        Set<String> oldSeqLineSet = headList.stream().map(a -> a.getLine() + "_" + a.getSeqNo()).collect(Collectors.toSet());
        for (CancelSchRequest r : request) {
            if (!oldSeqLineSet.contains(r.getLine() + "_" + r.getSeqNo())){
                throw new ServiceErrorException(r.getLine()+" 没有序号为"+r.getSeqNo()+"的数据");
            }
        }
        if (oldSeqLineSet.size()>headList.size()){
            throw new ServiceErrorException("出现重复序号，更新失败");
        }
        /*进行派工回滚*/
        headMapper.delete(headWrapper);
        lotMapper.schBackUp(headList,companyId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void reOrder() {
        String companyId = UserIdThread.get().getCompanyId();
        /*提取全部数据*/
        LambdaQueryWrapper<PsScheduleHead> headWrapper = new LambdaQueryWrapper<PsScheduleHead>()
                .eq(PsScheduleHead::getCompanyId,companyId);
        List<PsScheduleHead> oldHeadList = headMapper.selectList(headWrapper);
        /*删除原有数据*/
        headMapper.delete(headWrapper);
        /*数据重排*/
        List<String> lineList = oldHeadList.stream()
                .map(PsScheduleHead::getLine)
                .filter(Objects::nonNull)
                .distinct()
                .collect(Collectors.toList());
        List<PsScheduleHead> newHeadList = new ArrayList<>(oldHeadList.size());
        for (String line : lineList) {
            List<PsScheduleHead> newHeads;
            newHeads = oldHeadList.stream()
                    .filter(l -> line.equals(l.getLine()))
                    .sorted(Comparator.comparing(PsScheduleHead::getSeqNo))
                    .collect(Collectors.toList());
            BigDecimal seqNo = BigDecimal.ZERO;
            for (PsScheduleHead newHead : newHeads) {
                /*重新写入排好数据*/
                newHead.setSeqNo(seqNo);
                headMapper.insert(newHead);
            }
        }
    }

    @Override
    public List<String> brandBack(String brand) {
        String companyId = UserIdThread.get().getCompanyId();
        /*获取数据*/
        LambdaQueryWrapper<PsScheduleLot> wrapper = new LambdaQueryWrapper<PsScheduleLot>()
                .select(PsScheduleLot::getBrand)
                .like(StringUtils.isNotBlank(brand),PsScheduleLot::getBrand,brand)
                .isNotNull(PsScheduleLot::getBrand)
                .groupBy(PsScheduleLot::getBrand)
                .last(StringUtils.isNotBlank(brand),"ORDER BY IF(brand ='"+brand+"',0,1)");
        List<PsScheduleLot> brandList = lotMapper.selectList(wrapper);
        if (ArrayUtils.isEmpyt(brandList)){
            return new ArrayList<>();
        }
        return brandList.stream()
                .map(PsScheduleLot::getBrand)
                .collect(Collectors.toList());

    }

    @Override
    public void schUp() {
        String companyId = UserIdThread.get().getCompanyId();
        /*删除多余数据*/
        headMapper.schClean(companyId);
        /*更新数据*/
        headMapper.schUp(companyId);
    }

    /**
     * 安排进度前检测
     * @param lotNoList 指令集合
     * @param companyId 公司ID
     * @return 返回基础数据
     */
    private List<PsScheduleLot> schCheck(List<String> lotNoList,String companyId) {
        /*指令不能重复*/
        long lotSize =  lotNoList.stream().distinct().count();
        if (lotSize != lotNoList.size()){
            throw new ServiceErrorException("指令不能重复");
        }
        /*判断是否建档*/
        LambdaQueryWrapper<PcLotLtTime> ltWrapper = new LambdaQueryWrapper<PcLotLtTime>()
                .in(PcLotLtTime::getLotNo, lotNoList)
                .eq(PcLotLtTime::getCompanyId, companyId)
                .groupBy(PcLotLtTime::getLotNo);
        List<PcLotLtTime> ltTimeList = ltTimeMapper.selectList(ltWrapper);
        if (ArrayUtils.isEmpyt(ltTimeList)) {
            throw new ServiceErrorException("指令全部未在评审建档");
        }
        Set<String> findLotSet = ltTimeList.stream().map(PcLotLtTime::getLotNo).collect(Collectors.toSet());
        List<String> unFindLotNoList = lotNoList.stream().filter(a -> !findLotSet.contains(a)).collect(Collectors.toList());
        if (ArrayUtils.isNoEmpyt(unFindLotNoList)){
            throw new ServiceErrorException("指令" + unFindLotNoList.get(0)+"未在评审中建档");
        }
        List<PcLotLtTime> unDefine = ltTimeList.stream().filter(a->a.getLastPpd() == null ||
                        a.getStitchPpd() == null ||
                        a.getLastPpd().compareTo(BigDecimal.ZERO) <= 0)
                .collect(Collectors.toList());
        if (ArrayUtils.isNoEmpyt(unDefine)) {
            throw new ServiceErrorException("指令" + unDefine.get(0).getLotNo() + "未设置人均日产能");
        }
        /*获取基础数据*/
        LambdaQueryWrapper<PsScheduleLot> schLotWrapper = new LambdaQueryWrapper<PsScheduleLot>()
                .in(PsScheduleLot::getLotNo, lotNoList)
                .eq(PsScheduleLot::getCompanyId, companyId);
        List<PsScheduleLot> schLotList = lotMapper.selectList(schLotWrapper);
        if (ArrayUtils.isEmpyt(schLotList)){
            throw new ServiceErrorException("指令全部不存在");
        }
        /*判断是否存在*/
        List<String> schLotNoList = schLotList.stream().map(PsScheduleLot::getLotNo).collect(Collectors.toList());
        schLotNoList.removeAll(lotNoList);
        if (ArrayUtils.isNoEmpyt(schLotNoList)) {
            throw new ServiceErrorException("指令" + schLotNoList.get(0) + "不存在");
        }
        /*判断是否有重复*/
        if (schLotList.size()>lotNoList.size()){
            log.error("\n错误接口：schCheck\n错误原因：指令重复\n错误表：PC_SCHEDULE_LOT\n入参：lotNo:{}",lotNoList);
            throw new ServiceErrorException("同指令下存在多组数据");
        }
        return schLotList;
    }

    private List<PcCuttingLsHead> lsCheck(String lotNo, List<Integer> lsNoList, BigDecimal seqNo, String companyId){
        /*批次查重*/
        long count = lsNoList.stream().distinct().count();
        if (count!=lsNoList.size()){
            log.error("\n错误接口：lsCheck\n错误原因：批次重复\n问题所在表：PC_CUTTING_LS_HEAD\n错误数据：lotNo:{},lsNo:{}",lotNo,lsNoList);
            throw new ServiceErrorException("批次不能重复");
        }
        LambdaQueryWrapper<PcCuttingLsHead> lsWrapper= new LambdaQueryWrapper<PcCuttingLsHead>()
                .eq(PcCuttingLsHead::getLotNo,lotNo)
                .in(PcCuttingLsHead::getLsNo,lsNoList)
                .eq(PcCuttingLsHead::getCompanyId,companyId);
        List<PcCuttingLsHead> lsList = lsHeadMapper.selectList(lsWrapper);
        if (ArrayUtils.isEmpyt(lsList)){
            throw new ServiceErrorException("该指令下不存在这些批次");
        }
        Set<Integer> lsNoSet = lsList.stream()
                .map(PcCuttingLsHead::getLsNo)
                .collect(Collectors.toSet());
        List<Integer> unFindLsNoList = lsNoList.stream()
                .filter(lsNo -> !lsNoSet.contains(lsNo))
                .collect(Collectors.toList());
        if (ArrayUtils.isNoEmpyt(unFindLsNoList)){
            throw new ServiceErrorException("未查到该指令下存在"+unFindLsNoList.get(0)+"批次");
        }
        LambdaQueryWrapper<PsScheduleHead> seqSchHeadWrapper = new LambdaQueryWrapper<PsScheduleHead>()
                .eq(PsScheduleHead::getCompanyId, companyId)
                .eq(PsScheduleHead::getSeqNo, seqNo)
                .last("LIMIT 1");
        PsScheduleHead head = headMapper.selectOne(seqSchHeadWrapper);
        if (head!=null){
            throw new ServiceErrorException("第"+head.getSeqNo()+"批次已排产");
        }
        return lsList;
    }
}
